﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using Niknak.Services.TaskScheduler.Interfaces.DataModel;

namespace Sandbox.Data.Mongo
{
    public class AuditLog : IAuditLog<AuditLogEntry>
    {
        private static string COLLECTIONNAME = "AuditLog";
        private static object _AddEntryLock = new object();

        [BsonId]
        public Guid AuditLogId { get; set; }
        public Guid ScheduledTaskId { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime FinishDate { get; set; }
        public DateTime IterationDate { get; set; }
        public Niknak.Services.TaskScheduler.Enums.TaskOutcome TaskOutcome { get; set; }
        public Niknak.Services.TaskScheduler.Enums.CompletedEventStatus CompletedEventStatus { get; set; }
        public AuditLogEntry[] AuditLogEntries { get; set; }

        [BsonIgnore]
        private ScheduledTask _ScheduledTask = null;
        [BsonIgnore]
        public ScheduledTask ScheduledTask
        {
            get
            {
                if (_ScheduledTask == null)
                    _ScheduledTask = (ScheduledTask)ScheduledTask.GetByTaskId(this.ScheduledTaskId);
                return _ScheduledTask;
            }
        }

        [BsonIgnore]
        public bool _EnableLogging = true;
        [BsonIgnore]
        public bool EnableLogging
        {
            get { return _EnableLogging; }
            set { _EnableLogging = value; }
        }
        [BsonIgnore]
        public string EditPageUrl
        {
            get { return "AuditLog.aspx"; }
        }

        public void SaveAuditLog()
        {
            this.Save();
        }

        public void Save()
        {
            Helper.GetCollection<AuditLog>(COLLECTIONNAME).Save(this);
        }

        public void Remove(Guid logId)
        {
            Helper.GetCollection<AuditLog>(COLLECTIONNAME).Remove(
                Query.EQ("_id", logId)
            );
        }

        public void RemoveAll()
        {
            Helper.GetCollection<AuditLog>(COLLECTIONNAME).RemoveAll();
        }

        public void RemoveByTaskId(Guid taskId)
        {
            Helper.GetCollection<AuditLog>(COLLECTIONNAME).Remove(
                Query.EQ("ScheduledTaskId", taskId)
            );
        }

        public static void InsertBatch(List<AuditLog> auditLogs)
        {
            Helper.GetCollection<AuditLog>(COLLECTIONNAME).InsertBatch(auditLogs);
        }

        public static void UpsertBatch(List<AuditLog> auditLogs)
        {
            foreach (AuditLog auditLog in auditLogs)
                Helper.GetCollection<AuditLog>(COLLECTIONNAME).Save(auditLog);
        }

        public IAuditLog<AuditLogEntry> GetAuditLogById(Guid auditLogId)
        {
            return Helper.GetCollection<AuditLog>(COLLECTIONNAME).FindOne(
                Query.EQ("_id", auditLogId)
            );
        }

        public static AuditLog GetAuditLogByScheduledTaskIdAndIterationDate(Guid scheduledTaskId, DateTime iterationDate)
        {
            return Helper.GetCollection<AuditLog>(COLLECTIONNAME).FindOne(
                Query.And(
                    Query.EQ("ScheduledTaskId", scheduledTaskId),
                    Query.EQ("IterationDate", iterationDate)
                )
            );
        }

        public IEnumerable<IAuditLog<AuditLogEntry>> GetAllAuditLogs()
        {
            return Helper.GetCollection<AuditLog>(COLLECTIONNAME).FindAll();
        }

        public IEnumerable<IAuditLog<AuditLogEntry>> GetAuditLogsByScheduledTaskId(Guid scheduledTaskId)
        {
            return Helper.GetCollection<AuditLog>(COLLECTIONNAME).Find(
                Query.EQ("ScheduledTaskId", scheduledTaskId)
            );
        }

        /// <summary>
        /// Saving must be performed by the calling procedure
        /// </summary>
        public void AddEntry(Niknak.Services.TaskScheduler.Enums.AuditLogEntryType type, string message)
        {
            if (EnableLogging && this.AuditLogId == Guid.Empty)
                throw new Exception("AuditLog not yet saved");

            AuditLogEntry entry = new AuditLogEntry();
            entry.AuditLogEntryId = Guid.NewGuid();
            entry.EntryDate = DateTime.UtcNow;
            entry.AuditLogEntryType = type;
            entry.Message = message;

            if (EnableLogging)
            {
                lock (_AddEntryLock)
                {
                    List<AuditLogEntry> entries = new List<AuditLogEntry>();
                    entries.Add(entry);

                    if (this.AuditLogEntries != null && this.AuditLogEntries.Any())
                        entries.AddRange(this.AuditLogEntries);

                    this.AuditLogEntries = entries.ToArray();
                }
            }

#if DEBUG
            switch (type)
            {
                case Niknak.Services.TaskScheduler.Enums.AuditLogEntryType.Information:
                    Console.ForegroundColor = ConsoleColor.White;
                    break;
                case Niknak.Services.TaskScheduler.Enums.AuditLogEntryType.Warning:
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    break;
                case Niknak.Services.TaskScheduler.Enums.AuditLogEntryType.Error:
                    Console.ForegroundColor = ConsoleColor.Red;
                    break;
            }

            Console.WriteLine(message);
            Console.ResetColor();
#endif
        }
    }

    public class AuditLogEntry : Niknak.Services.TaskScheduler.Interfaces.DataModel.IAuditLogEntry
    {
        [BsonId]
        public Guid AuditLogEntryId { get; set; }
        public DateTime EntryDate { get; set; }
        public Niknak.Services.TaskScheduler.Enums.AuditLogEntryType AuditLogEntryType { get; set; }
        public string Message { get; set; }
    }
}
